package tue.cse.advanceddatabases.project;

import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.logging.Logger;

import javax.faces.context.FacesContext;
import javax.faces.model.SelectItem;

import org.apache.commons.httpclient.NameValuePair;
import org.dom4j.Element;

import tue.cse.advanceddatabases.project.xml.album.Album;
import tue.cse.advanceddatabases.project.xml.album.AlbumsParser;
import tue.cse.advanceddatabases.project.xml.artist.ArtistsParser;
import tue.cse.advanceddatabases.project.xml.genre.Genre;
import tue.cse.advanceddatabases.project.xml.genre.GenreParser;

public class AlbumsEngineBean extends MusicEngineBean {
	
	private Logger logger = Logger.getLogger(AlbumsEngineBean.class.getName());
	private List<Album> albums;

	private String albumToSearch;
	private String orderBySearch;
	//Goran
	private Album _album = new Album();

	/*
	 * public properties for private properties
	 */
	
	//Goran
	public Album getDefaultAlbum(){
	  	return _album;
	}
	
	public String getAlbumToSearch() {
		return albumToSearch;
	}
	
	public String getOrderBySearch() {
		return orderBySearch;
	}

	public void setOrderBySearch(String orderBySearch) {
		this.orderBySearch = orderBySearch;
	}	
		

	public void setAlbumToSearch(String albumToSearch) {
		this.albumToSearch = albumToSearch;
	}
	
	/*
	 * Actions: public methods (query constructions)
	 */
	
	/** return albums for "albumToSearch" **/
	public List<Album> getAlbums(){
		
		return albums;
	}
	
	/** search albums by "albumToSearch" property **/
	public void searchAlbums(){
		albums = null;
		logger.info("Searching albums by Name:" + albumToSearch + " and order by " + orderBySearch);
		
		NameValuePair[] parameters = new NameValuePair[2];
		parameters[0] = new NameValuePair("name",albumToSearch);
		parameters[1] = new NameValuePair("order",orderBySearch);
		
		String result = executeQueryXQL("albumsQueryByName.xql",parameters);
		
		
		if (result != null){
			Element albumsElement = stringToXml(result);
			if (albumsElement!=null){
				logger.info("Albums are contained in" + albumsElement);
				albums = AlbumsParser.parse(albumsElement);
				return;
			}			
		}
		
		albums = null;
		
	}
	
	/** search albums by "albumKey" parameter **/
	/** return current album searched by id **/
	public Album getAlbum(){
		String albumId = getAlbumKey();
		
		String album = searchAlbumId(albumId);
		
		if (album != null){
			List<Element> albumsElement = removeExistDBElement(album);
			if (albumsElement!=null){
				logger.info("Albums are contained in" + albumsElement);
				List<Album> albumArray = AlbumsParser.parse(albumsElement);
				// Return always the first.
				return albumArray.get(0);
			}
		}
		
		return null;		
	}
	
	public List<Album> getAlbumsByArtist(){
		String artistId = getArtistKey();
		
		logger.info(" Search Album by Artist:" + artistId);

		String query = queryBegin() + albumsByArtistQuery(artistId) + queryEnd();
		String result = executeQuery(query,"Albums.xml");
		
		if (result != null){
			List<Element> albumsElement = removeExistDBElement(result);
			if (albumsElement!=null){
				logger.info("Albums are contained in" + albumsElement);
				return AlbumsParser.parse(albumsElement);
			}
		}
		
		return null;		
		
	}
	
	/** get Top rated Album List **/
	public List<Album> getTopRatedAlbumsRelated(){
		logger.info("Searching for getTopRatedAlbumsRelated");
		String albumId = getAlbumKey();
		String artistId = getAlbum().getArtistId();
		
		NameValuePair[] parameters = new NameValuePair[3];
		parameters[0] = new NameValuePair("artistId",artistId);
		parameters[1] = new NameValuePair("albumId",albumId);
		parameters[2] = new NameValuePair("count","4");
		
		String result = executeQueryXQL("topRatedAlbumsRelated.xql",parameters);
		
		
		if (result != null){
			Element albumsElement = stringToXml(result);
			if (albumsElement!=null){
				logger.info("Albums are contained in" + albumsElement);
				return AlbumsParser.parse(albumsElement);
			}			
		}
		
		return null;
	}

	/** get Years data  **/
	public List<SelectItem> getYearsList(){
	  	ArrayList selectItems = new ArrayList<SelectItem>();
	  	
	  	Date d = new Date();
	  	int year = d.getYear() + 1900;
	  	
	  	for (int i = year; i > 1900; i--){
	  	  	selectItems.add(new SelectItem(Integer.toString(i), Integer.toString(i)));
	  	}
	  	
	  	return selectItems; 
	}
	
	/** go to add manageAlbum by "artistKey" parameter **/
	public String accessAddAlbum(){
	  	Clear();
	  	_album.setArtistId(getArtistKey());
    	    	return "manageAlbum";
	}
	
	/** go to edit Album by "albumKey" parameter **/
	public String editAlbum(){
    	    	this._album = getAlbum();
    	    	setIsNew(false);
    	    	return "manageAlbum";
	}
	
	/** remove Album by "albumKey" parameter **/
	public void removeAlbum(){
    	    if (deleteAlbum(getAlbumKey())){
    	        searchAlbums();
                status = "Item deleted.";
            }
            else{
                status = "Error while deleting!";
            }	
	}
	
	
	/** add genre **/
	public void addGenre(){
	  	logger.info("Inserting Genre");
	     
	  	String command = getInsertGenreForAlbumCommand(getDefaultAlbum().getId(), getDefaultAlbum().getGenre());
		
	  	String query = updateBegin() + command + updateEnd();
	  	String result = executeQuery(query, "Albums.xml");
		
	  	logger.info("Inserting result is:" + result);
	}
	
	/** remove genre by "genreKey" parameter from album**/
	public void removeGenre(){
	        FacesContext facesContext = FacesContext.getCurrentInstance();
		String genre = (String) facesContext.getExternalContext().getRequestParameterMap().get("genreKey");
    	    	
		logger.info("Deleting Genre");
		     
		String command = getDeleteGenreCommand(getDefaultAlbum().getId(), genre);
			
		String query = updateBegin() + command + updateEnd();
		String result = executeQuery(query, "Albums.xml");
			
		logger.info("Deleting result is:" + result);
	}
	
	/** get all genres on Album**/
	public List<Genre> getGenresByAlbum(){
	  	logger.info(" Get Genres for Album ID: " + getDefaultAlbum().getId());
		
		String query = queryBegin() + getAllGenresCommand(getDefaultAlbum().getId()) + queryEnd();
		String result = executeQuery(query,"Albums.xml");
		
		if (result != null){
		  	List<Element> genresElement = removeExistDBElement(result);
			if (genresElement!= null){
				logger.info("Genres are contained in" + genresElement);
				return GenreParser.parse(genresElement);
			}
		}

		return null;
	}
	
	
	//Goran
	public void btnSave(){
	  
              if (getIsNew()){
                  if (insertAlbum()){
                      status = "Item saved.";
                  }
                  else{
                      status = "Error while saving!";
                  }
              }
              else{
                  if (updateAlbum()){
                      status = "Item saved.";
                  }
                  else{
                    	status = "Error while saving!";
                  }
              }
	}
	
	//Goran
	public void btnDelete(){
	  
	    if (deleteAlbum(getDefaultAlbum().getId())){
                SetupForm();
                status = "Item deleted.";
            }
            else{
                status = "Error while deleting!";
            }
	}
	
	//Goran
	public void btnNew(){
	     SetupForm();    
	}
	
	
	/** Clear values that are used in the bean **/
	private void Clear()
	{   
	    String artistId = _album.getArtistId(); 
	    _album = new Album();
	    if (artistId != null)
	      _album.setArtistId(artistId);
	    setIsNew(true);
	}
	
	private void SetupForm()
	{
	    Clear();
	    status = "";
	}
	
	/*
	 * Private Query constructions
	 */
	
	private String searchAlbumId(String albumId){
		logger.info("Searching album by ID:");

		
		logger.info(" Search Album ID:" + albumId);

		String query = queryBegin() + albumIdQuery(albumId) + queryEnd();
		String result = executeQuery(query,"Albums.xml");
		
		if (!("fail".equalsIgnoreCase(result)))
			return result;

		return null;
	}
	
	//Goran
	private boolean insertAlbum(){
	     
	     logger.info("Inserting Album");

	     //TODO: generate albumId
	     _album.setId(_album.getName());
	     
	     SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
	     String dateEntered = sdf.format(new Date());
	     
	     String command = getInsertAlbumCommand(getDefaultAlbum().getId(), getDefaultAlbum().getName(),
		 	getDefaultAlbum().getImage(), getDefaultAlbum().getArtistId(),
		 	getDefaultAlbum().getYear(), getDefaultAlbum().getLaunchdate(),  
		 	dateEntered, getDefaultAlbum().getNumberOfTracks());
			     
	     String query = updateBegin() + command + updateEnd();
	     String result = executeQuery(query, "Albums.xml");
		
	     logger.info("Inserting result is:" + result);
	     
	     if (!("fail".equalsIgnoreCase(result))){
	       	  setIsNew(false);	
		  return true;
	     }
	       
	     return false;
	}
	
	//Goran
	private boolean updateAlbum(){
	   
	     logger.info("Updating Albums");

	     String command = getUpdateAlbumCommand(getDefaultAlbum().getId(), getDefaultAlbum().getName(), 
		 	getDefaultAlbum().getImage(), getDefaultAlbum().getArtistId(),
		 	getDefaultAlbum().getYear(), getDefaultAlbum().getLaunchdate(),  
		 	getDefaultAlbum().getNumberOfTracks());
		
	     String query = updateBegin() + command + updateEnd();
	     String result = executeQuery(query, "Albums.xml");
		
	     logger.info("Inserting result is:" + result);
	     
	     if (!("fail".equalsIgnoreCase(result))){
	       	  setIsNew(false);	
		  return true;
	     }
	       
	     return false;
	}
	
	//Goran
	private boolean deleteAlbum(String albumId){
	  
	     logger.info("Deleting Artist"); 	
	     
	     String command = getDeleteAlbumCommand(albumId);
		
	     String query = updateBegin() + command + updateEnd();
	     String result = executeQuery(query, "Albums.xml");
		
	     logger.info("Deleting result is:" + result);
		
	     if (!("fail".equalsIgnoreCase(result))){
	       	  setIsNew(false);	
		  return true;
	     }
	       
	     return false;
	}
	
	/*
	 * Queries
	 */
	
	/** album id query **/
	private String albumIdQuery(String albumId){
		return "element { \"albums\" }{ " +
			"for $album in //album " +
			"where $album/@id =\"" + albumId + "\" " + 
			"return $album" +
			"}";
	}
	
	private String albumsByArtistQuery(String artistId){
		return "element { \"albums\" }{ " +
		"for $album in //album " +
		"where $album/artist_id/text() =\"" + artistId + "\" " + 
		"return $album" +
		"}";		
	}
	
	/** insert album command **/
	private String getInsertAlbumCommand(String albumId, String name, String image, String artistId,  
	    	String year, String launchDate,  String dateEntered, String numberOfTracks){
		return "<xu:append select=\"/albums\">" + 
				"<xu:element name=\"album\">" +
				"<xu:attribute name=\"id\">" + albumId + "</xu:attribute>" +
				"<name>" + name + "</name>" +
				"<image>" + image + "</image>" +
				"<artist_id>" + artistId + "</artist_id>" +
				"<year>" + year + "</year>" +
				"<launchdate>" + launchDate + "</launchdate>" +
				"<date_created>" + dateEntered + "</date_created>" +
				"<genres></genres>" +
				"<numberOfTracks>" + numberOfTracks + "</numberOfTracks>" +
				"<rating>0</rating>" +
				"<rating-count>0</rating-count>" +
				"<comment-count>0</comment-count>" +
				"</xu:element></xu:append>";
	}
	
	/** update album command **/
	private String getUpdateAlbumCommand(String albumId, String name, String image, String artistId,  
	    	String year, String launchDate, String numberOfTracks){
		return "<xu:update select=\"/albums/album[@id=\'" + albumId + "\']/name\">" +
		            name +
		       "</xu:update>" +
		       "<xu:update select=\"/albums/album[@id=\'" + albumId + "\']/image\">" +
		            image +
		       "</xu:update>" +
		       "<xu:update select=\"/albums/album[@id=\'" + albumId + "\']/artist_id\">" +
		            artistId +
		       "</xu:update>" +
		       "<xu:update select=\"/albums/album[@id=\'" + albumId + "\']/year\">" +
		            year +
		       "</xu:update>" +
		       "<xu:update select=\"/albums/album[@id=\'" + albumId + "\']/launchdate\">" +
		            launchDate +
		       "</xu:update>" +
		       "<xu:update select=\"/albums/album[@id=\'" + albumId + "\']/numberOfTracks\">" +
		       	    numberOfTracks +
		       "</xu:update>";
	}
	
	/** delete album (and comments on that album) command **/
	private String getDeleteAlbumCommand(String albumId){
		return "<xu:remove select=\"/albums/album[@id = \'" + albumId + "\']\" />" +
		       "<xu:remove select=\"/comments/comment[music_id = \'" + albumId + "\']\" />";
	}

	/** insert genre for artist command **/
	private String getInsertGenreForAlbumCommand(String albumId, String genre){
	  	return "<xu:append select=\"/albums/album[@id=\'" + albumId + "\']/genres\">" + 
	  		"<xu:element name=\"genre\">" + genre +
	  		"</xu:element></xu:append>";
	}
	
	/** delete genre for artist command **/
	private String getDeleteGenreCommand(String albumId, String genre){
		return "<xu:remove select=\"/albums/album[@id = \'" + albumId + "\']/genres/genre[text() = \'" + genre + "\']\" />";
	}	
	
	/** get all artist genres command **/
	private String getAllGenresCommand(String albumId){
	  	return "element { \"albums\" }{ " +
	  		"for $album in //album " +
	  		"where $album/@id =\"" + albumId + "\" " +
	  		"return for $genre in $album/genres/genre " + 
	  		        "return $genre" +
	  		"}";
	}
}
